#include <errno.h>
#include <cstring>
#include <sstream>
#include <iostream>
#include "Wrappers/Pthread/ConditionVariable.hpp"
#include "Wrappers/Pthread/ThreadException.hpp"

namespace wrappers {
namespace pthread {

ConditionVariable::ConditionVariable() {
  if (pthread_cond_init(&mCondition, 0) != 0) {
    throw ThreadException(
	"ConditionVariable: failed to initialize the condition variable");
  }
}

ConditionVariable::~ConditionVariable() {
  pthread_cond_destroy(&mCondition);
}

void ConditionVariable::NotifyAll() {
  int retval = pthread_cond_broadcast(&mCondition);
  std::ostringstream oss;

  if (retval != 0) {
    oss << "ConditionVariable: cannot broadcast on the current condition "
	<< "variable (" << strerror(retval) << ")";
    throw ThreadException(oss.str());
  }
}

void ConditionVariable::NotifyOne() {
  int retval = pthread_cond_signal(&mCondition);
  std::ostringstream oss;

  if (retval != 0) {
    oss << "ConditionVariable: cannot signal on the current condition "
	<< "variable (" << strerror(retval) << ")";
    throw ThreadException(oss.str());
  }
}

void ConditionVariable::Wait(Mutex &mutex) {
  std::ostringstream oss;

  mutex.Lock();
  int retval = pthread_cond_wait(&mCondition, &mutex.GetMutex());
  mutex.Unlock();
  if (retval != 0) {
    oss << "ConditionVariable: cannot wait on the current condition "
	<< "variable (" << strerror(retval) << ")";
    throw ThreadException(oss.str());
  }
}

bool ConditionVariable::TimedWait(Mutex &mutex,
    const struct timespec &timeOut) {
  std::ostringstream oss;

  mutex.Lock();
  int retval = pthread_cond_timedwait(&mCondition, &mutex.GetMutex(), &timeOut);
  mutex.Unlock();
  if (retval == ETIMEDOUT)
    return true;
  else if (retval != 0) {
    oss << "ConditionVariable: cannot wait on the current condition "
	<< "variable (" << strerror(retval) << ")";
    throw ThreadException(oss.str());
  }
  return false;
}

pthread_cond_t &ConditionVariable::GetCondition() {
  return mCondition;
}

}
}
